package pdh

import (
	"context"
	"log"
	"sort"
	"time"
)

type Performance struct {
	ctx      context.Context
	ctxFunc  context.CancelFunc
	query    Query
	counters map[string]*ObjectCounterValue
	opened   bool
}

func NewPerformance() *Performance {
	ctx, ctxFunc := context.WithCancel(context.Background())
	return &Performance{
		ctx:      ctx,
		ctxFunc:  ctxFunc,
		query:    NewQuery(),
		counters: map[string]*ObjectCounterValue{},
	}
}

func (tis *Performance) AddCounter(names ...*CounterName) {
	if tis.opened {
		log.Println("have opened")
		return
	}

	for _, value := range names {
		tis.counters[value.Name] = newObjectCounterValue(tis.query, *value)
	}
}

func (tis *Performance) GetQuery() Query {
	return tis.query
}

func (tis *Performance) Open(onCollectData func(data []*ObjectCounterValue), afterOpenQuery ...func(ins *Performance)) error {
	var ok = false
	defer func() {
		if !ok {
			tis.close()
		}
	}()

	err := tis.query.Open()
	if err != nil {
		return err
	}

	for _, fn := range afterOpenQuery {
		fn(tis)
	}

	tis.opened = true

	var names []string
	for _, value := range tis.counters {
		value.hCounter, err = tis.query.AddCounter(value.Name)
		if err != nil {
			log.Printf("add counter fail. %v", value.Name)
			names = append(names, value.Name)
			continue
		}
	}

	for _, name := range names {
		delete(tis.counters, name)
	}

	go tis.run(onCollectData)

	ok = true
	return nil
}

func (tis *Performance) run(onCollectData func(data []*ObjectCounterValue)) {
	query := tis.query

	defer func() {
		tis.close()
	}()

	_ = query.CollectData()
	for {
		select {
		case <-tis.ctx.Done():
			return
		default:
		}

		time.Sleep(time.Second)
		_ = query.CollectData()

		var values []*ObjectCounterValue
		for _, counter := range tis.counters {
			if err := counter.getData(); err != nil {
				// log.Printf("get data fail. %v %v", counter.Name, err)
			} else {
				value := counter
				values = append(values, value)
			}
		}

		sort.Slice(values, func(i, j int) bool {
			return values[i].Name < values[j].Name
		})

		onCollectData(values)
	}
}

func (tis *Performance) close() {
	for _, counter := range tis.counters {
		counter.close()
	}
	_ = tis.query.Close()

	tis.ctxFunc()
}

func (tis *Performance) Close() {
	tis.ctxFunc()
}
